#include <string.h>
#include "zbacktrace.h"
#include "zluaobj.h"
#include "zobj.h"

int zLoadFile(const char* dir, const char* file,zString& context)
{
	zString diffile(dir);
	diffile+=file;
	FILE* fp=fopen(diffile.c_str(),"rb");
	if (fp)
	{
		fseek(fp,0,SEEK_END);
		int len=ftell(fp);
		if (len > 0)
		{
			fseek(fp,0,SEEK_SET);
			context.alloc(len+1);
			if (context.mlen>=len)
			{
				if (len==fread(context.data,1,len,fp))
				{
					context.slen=len;
					context.data[context.slen]='\0';
					fclose(fp);
					return 0;
				}
			}
		}
		fclose(fp);
		context="";
		if (len==0)
			return 0;
	}
	return -1;
}


zLuaObj::zLuaObj(lua_State* L)
{
	init(L);
}
zLuaObj::zLuaObj(lua_State* L,const bool eattop)
{
	m_L=L;
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
}
void zLuaObj::init(lua_State* L)
{
	m_L = L;
	lua_pushnil(m_L);
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	return;
}
void zLuaObj::SetNil()
{
	if (m_L)
	{
		luaL_unref(m_L,LUA_REGISTRYINDEX,m_ref);
		lua_pushnil(m_L);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	}
}
void zLuaObj::setString(const char* val)
{
	if (m_L)
	{
		luaL_unref(m_L,LUA_REGISTRYINDEX,m_ref);
		lua_pushstring(m_L,val);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	}
}
void zLuaObj::setTable(int inum,int hnum)
{
	if (m_L)
	{
		luaL_unref(m_L,LUA_REGISTRYINDEX,m_ref);
		lua_createtable(m_L,inum,hnum);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	}
}
zLuaObj::zLuaObj(lua_State* L,const char* global)
{
	m_L = L;
	lua_getglobal(L,global);
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
}

zLuaObj::zLuaObj(lua_State* L,int idx)
{
	m_L=L;
	registerValue(idx);
}

void zLuaObj::linkTo(lua_State* L,const char* global)
{
	if (m_L)
	{
		luaL_unref(m_L,LUA_REGISTRYINDEX,m_ref);
	}
	m_L = L;
	lua_getglobal(L,global);
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
}
void zLuaObj::linkTo(lua_State* L,int idx)
{
	if (m_L)
	{
		luaL_unref(m_L,LUA_REGISTRYINDEX,m_ref);
	}
	m_L=L;
	registerValue(idx);
}
void zLuaObj::linkTo(const zLuaObj& org)
{
	if (m_L)
	{
		luaL_unref(m_L,LUA_REGISTRYINDEX,m_ref);
	}
	m_L=org.m_L;
	org.push();
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
}

zLuaObj::zLuaObj(const zLuaObj& parent, const char* name)
{
	m_L = parent.m_L;

	if (!parent.isTable() && !parent.isUserdata())
	{
		lua_pushnil(m_L);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
		return;
	}

	parent.push();
	lua_pushstring(m_L,name);
	lua_gettable(m_L,-2);
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	lua_pop(m_L,1);
}
zLuaObj::zLuaObj(const zLuaObj* pparent, const char* name)
{
	m_L = pparent->m_L;

	if (!pparent->isTable() && !pparent->isUserdata())
	{
		lua_pushnil(m_L);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
		return;
	}

	pparent->push();
	lua_pushstring(m_L,name);
	lua_gettable(m_L,-2);
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	lua_pop(m_L,1);
}
int zLuaObj::setFieldS(const char* name,const char* val)
{
	if (!isTable() && !isUserdata())
	{
		return -1;
	}
	push();
	lua_pushstring(m_L,name);
	lua_pushstring(m_L,val);
	lua_settable(m_L,-3);
	lua_pop(m_L,1);
	return 0;
}
void zLuaObj::linkTo(const zLuaObj& parent, const char* name)
{
	if (m_L)
	{
		luaL_unref(m_L,LUA_REGISTRYINDEX,m_ref);
	}
	m_L = parent.m_L;

	if (!parent.isTable() && !parent.isUserdata())
	{
		lua_pushnil(m_L);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
		return;
	}

	parent.push();
	lua_pushstring(m_L,name);
	lua_gettable(m_L,-2);
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	lua_pop(m_L,1);
}
zLuaObj::zLuaObj(const zLuaObj& parent, int idx)
{
	m_L = parent.m_L;

	if (!parent.isTable() && !parent.isUserdata())
	{
		lua_pushnil(m_L);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
		return;
	}

	parent.push();
	lua_pushinteger(m_L,idx);
	lua_gettable(m_L,-2);
	m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	lua_pop(m_L,1);
}
zLuaObj::zLuaObj(const zLuaObj& parent, const zLuaObj& idx)
{
	m_ref=0;
	if (m_L == parent.m_L)
	{
		if (!parent.isTable() && !parent.isUserdata())
		{
			lua_pushnil(m_L);
			registerValue(-1);
			lua_pop(m_L,1);
			return;
		}
		parent.push();
		idx.push();
		lua_gettable(m_L,-2);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
		lua_pop(m_L,1);
	}
}
zLuaObj::~zLuaObj()
{
	if (m_L)
	{
		luaL_unref(m_L,LUA_REGISTRYINDEX,m_ref);
	}
}

int zLuaObj::call_va(int (*ptb)(struct lua_State *L), const char *sig, ...)
{
	int re;
	va_list arglist;
	va_start(arglist, sig);
	re = call_err(NULL,ptb,sig,arglist);
	va_end(arglist);
	return re;
}

int zLuaObj::call_va_err (zString* err, int (*ptb)(struct lua_State *L), const char *sig, ...)
{
	int re;
	va_list arglist;
	va_start(arglist, sig);
	re = call_err(err,ptb,sig,arglist);
	va_end(arglist);
	return re;
}
int zLuaObj::call_rva(int tracebackref, const char *sig, ...)
{
	int re;
	va_list arglist;
	va_start(arglist, sig);
	re = call_rerr(NULL,tracebackref,sig,arglist);
	va_end(arglist);
	return re;
}

int zLuaObj::call_rva_err (zString* err, int tracebackref, const char *sig, ...)
{
	int re;
	va_list arglist;
	va_start(arglist, sig);
	re = call_rerr(err,tracebackref,sig,arglist);
	va_end(arglist);
	return re;
}
int zLuaObj::call_err (zString* err, int (*ptb)(struct lua_State *L), const char *sig, va_list vl)
{
	return call_rp_err_(err,-1,ptb,sig,vl);
}
int zLuaObj::call_rerr (zString* err, int tracebackref, const char *sig, va_list vl)
{
	return call_rp_err_(err,tracebackref,NULL,sig,vl);
}
int zLuaObj::call_rp_err_(zString* err,int tracebackref, int (*ptb)(struct lua_State *L),const char *sig, va_list vl)
{
	int narg, nres; /* number of arguments and results */
	STACKKEEPER _K(m_L);
	int traceback;
	if (tracebackref>=0)
	{
		lua_rawgeti(m_L,LUA_REGISTRYINDEX,tracebackref);
		traceback=_K.getsavedpos()+1;
	}
	else if (ptb)
	{
		lua_pushcfunction(m_L,ptb);
		traceback=_K.getsavedpos()+1;
	}
	else
		traceback=0;
	push(); /* get function */
	/* push arguments */
	narg = 0;
	while (*sig)
	{ /* push arguments */
		switch (*sig++)
		{
		case 'd': /* double argument */
			lua_pushnumber(m_L, va_arg(vl, double));
			break;
		case 'l': /* double argument */
			lua_pushnumber(m_L, va_arg(vl, long long));
			break;
		case 'i': /* int argument */
			lua_pushinteger(m_L, va_arg(vl, int));
			break;
		case 'b': /* int argument */
			lua_pushboolean(m_L, va_arg(vl, int));
			break;
		case 's': /* string argument */
			lua_pushstring(m_L, va_arg(vl, char *));
			break;
		case 'B': /*binary data*/
			{
				const char* buf=va_arg(vl, char *);
				int len=va_arg(vl, int);
				lua_pushlstring(m_L, buf, len);	
			}
			break;
		case 'O':
			{
				zLuaObj* tmp=va_arg(vl, zLuaObj*);
				if (tmp)
					tmp->push();
				else
					lua_pushnil(m_L);
			}
			break;
		case 'C':
			lua_pushcfunction(m_L, va_arg(vl, lua_CFunction));
			break;
		case 'I':
			lua_pushvalue(m_L,va_arg(vl, int));
			break;
		case 'L':
			lua_pushlightuserdata(m_L, va_arg(vl, void*));
			break;
		case 'V':
			{
				zVar* ppa=va_arg(vl, zVar*);
				if (!ppa)
					lua_pushnil(m_L);
				else
				{
					if(ppa->IsTable())
					{
						zVarTable& tt = ppa->Table();
						unsigned int numargs=tt.count();
						if (numargs>0)
						{
							for(unsigned int cnt=0;cnt<numargs;cnt++)
							{
								zVarToLua(m_L,tt[cnt].v);
							}
							narg+=numargs-1;
						}
						else
						{
							lua_pushnil(m_L);
						}
					}
					else
					{
						zVarToLua(m_L,*ppa);
					}
				}
			}
			break;
		case 'A':
			{
				zVar* ppa=va_arg(vl, zVar*);
				if (ppa)
					zVarToLua(m_L,*ppa);
				else
					lua_pushnil(m_L);
			}
			break;
		case 'N':
				lua_pushnil(m_L);
			break;
		case '>':
			goto endwhile;
		default:
			zDbgOut("invalid option (%c)", *(sig - 1));
			if (err)
				err->format("EICO1:%c", *(sig - 1));
			return -1;
		}
		narg++;
		if (!lua_checkstack(m_L, 1))
		{
			char bufbt[512+32];
			zGetBacktrace(bufbt,512);
			bufbt[511]='\0';
			zlogger_error(NULL,"lua stack overflow (too many params),\n%s",bufbt);
			if (err)
				*err="ESOTMP";
			return -1;
		}
	} endwhile:
	/* do the call */
	nres = strlen(sig); /* number of expected results */
	int ctop=lua_gettop(m_L)-narg-1;
	if (lua_pcall(m_L, narg, -1,traceback) != 0) /* do the call */
	{
		zlogger_error(NULL,"error running function `': %s", lua_tostring(m_L, -1));
		if (err)
			err->format("EERF1':%s", lua_tostring(m_L, -1));
		return -1;
	}
	int ctop2=lua_gettop(m_L)-ctop;
	if (nres > ctop2 && !lua_checkstack(m_L,nres-ctop2))
	{
		char bufbt[512+32];
		zGetBacktrace(bufbt,512);
		bufbt[511]='\0';
		zlogger_error(NULL,"stack overflow for return args,\n%s",bufbt);
		if (err)
			*err="ESOFRA";
		return -1;
	}
	if (nres>ctop2)
	{
		lua_settop(m_L,ctop+nres);
	}
	if (nres<ctop2)
	{
		nres=ctop2;
	}
	/* retrieve results */
	nres = -nres; /* stack index of first result */
	while (*sig) { /* get results */
		switch (*sig++)
		{
		case 'd': /* double result */
			if (!lua_isnumber(m_L, nres))
			{
				zLoggerOut("wrong result type number needed");
				if (err)
					err->format("EWRT1N:number");
				return -1;
			}
			*va_arg(vl, double *) = lua_tonumber(m_L, nres);
			break;
		case 'i': /* int result */
			if (!lua_isnumber(m_L, nres))
			{
				zLoggerOut("wrong result type integer needed");
				if (err)
					err->format("EWRT1N:integer");
				return -1;
			}
			*va_arg(vl, int *) = (int)lua_tointeger(m_L, nres);
			break;
		case 'l': /* int result */
			if (!lua_isnumber(m_L, nres))
			{
				zLoggerOut("wrong result type integer needed");
				if (err)
					err->format("EWRT1N:integer");
				return -1;
			}
			*va_arg(vl, long long *) = (long long)lua_tonumber(m_L, nres);
			break;
		case 's': /* string result */
			if (!lua_isstring(m_L, nres))
			{
				zLoggerOut("wrong result type string needed");
				if (err)
					err->format("EWRT1N:string");
				return -1;
			}
			*va_arg(vl, const char **) = lua_tostring(m_L, nres);
			break;
		case 'S': /* string result */
			{
				zString* ppa=va_arg(vl, zString*);
				if (ppa)
				{
					if (!lua_isstring(m_L, nres))
					{
						zLoggerOut("wrong result type string needed");
						if (err)
							err->format("EWRT1N:string");
						return -1;
					}
					*ppa=lua_tostring(m_L, nres);
				}
			}
			break;
		case 'b':
			/*if (!lua_isboolean(m_L, nres))
			{
				zLoggerOut("wrong result type boolean needed");
				if (err)
					err->format("wrong result type boolean needed");
				return -1;
			}*/
			*va_arg(vl, int *) = (int)lua_toboolean(m_L, nres);
			break;
		case 'O':
			va_arg(vl, zLuaObj*)->linkTo(m_L,nres);
			break;
		case 'A':
			{
				zVar* ppa=va_arg(vl, zVar*);
				if (ppa)
					zVarFromLua(m_L,*ppa,nres);
			}
			break;
		case 'V':
			{
				zVar* ppa=va_arg(vl, zVar*);
				if (ppa)
					zVarFromLuaVa(m_L,*ppa,nres,200);
			}
			break;
		default:
			zLoggerOut("invalid option (%c)\n", *(sig - 1));
			if (err)
				err->format("EICO1:%c", *(sig - 1));
			return -1;
		}
		nres++;
	}
	return 0;
}
#include "lj_alloc.h"
static void l_message (const char *pname, const char *msg)
{
	if (pname)
	{
		zlogger_error(NULL,"%s: %s\n",pname,msg);
	}
	else
	{
		zlogger_error(NULL,"%s\n",msg);
	}

}
int ZLuaEnv::report (int status)
{
	if (status && !lua_isnil(m_L, -1)) 
	{
		if (m_delegate)
		{
			m_delegate->OnErrReport(*this,status);
		}
		const char *msg = lua_tostring(m_L, -1);
		if (msg == NULL) msg = "(error object is not a string)";
		l_message(NULL, msg);
		lua_pop(m_L, 1);
	}
	return status;
}

int zlua_Name2EPATH(const char* p)
{
	if (!p)
		p="";
	if (strcmp(p,"ROOT")==0)
		return ZLuaEnv::ZPATH_ROOT;
	else if (strcasecmp(p,"MOD")==0)
		return ZLuaEnv::ZPATH_ROOT;
	else if (strcasecmp(p,"APP")==0)
		return ZLuaEnv::ZPATH_APP;
	else if (strcasecmp(p,"DOC")==0)
		return ZLuaEnv::ZPATH_DOC;
	else if (strcasecmp(p,"CACHE")==0)
		return ZLuaEnv::ZPATH_CACHE;
	else if (strcasecmp(p,"TMP")==0)
		return ZLuaEnv::ZPATH_TMP;
	else
		return ZLuaEnv::ZPATH_DEFAULT;
}
ZLuaEnv::ZLuaEnv(ZLuaEnvDelegate* pd)
:m_delegate(pd),m_ownsL(1)
{
#if 0
	m_lj_alloc=lj_alloc_create();
	m_L = lua_newstate(lj_alloc_f,m_lj_alloc);
#else
	m_lj_alloc=NULL;
	m_L=lua_open();
	luaL_openlibs(m_L);
#endif
	ZObj_lua_meta(m_L);
	lua_pop(m_L,1);
	if (m_delegate)
	{
		m_delegate->OnCreateLuaEnv(*this);
	}
}

ZLuaEnv::ZLuaEnv( ZLuaEnvDelegate* pd, lua_State* L ):m_delegate(pd), m_ownsL(0)
{
	m_lj_alloc=NULL;
	m_L=L;

	ZObj_lua_meta(m_L);
	lua_pop(m_L,1);
	if (m_delegate)
	{
		m_delegate->OnCreateLuaEnv(*this);
	}
}

ZLuaEnv::~ZLuaEnv()
{
	if (m_L)
	{
		if (m_ownsL)
		{
			lua_close(m_L);
		}
		m_L=NULL;
		if (m_lj_alloc)
		{
#if 0
			lj_alloc_destroy(m_lj_alloc);
#endif
			m_lj_alloc=NULL;
		}
		if (m_delegate)
		{
			m_delegate->OnCloseLuaEnv(*this);
		}
	}
}

static zString dp="./";
const zString& ZLuaEnv::GetPath(EPATH p)
{
	return m_delegate?m_delegate->GetPath(p):dp;
}
int ZLuaEnv::loadAT(const char* filename,zVar& ov,zVar* pin,EPATH p)
{
	int err;
	zString fc;
	if ((err=zLoadFile(m_delegate?m_delegate->GetPath(p):"",filename,fc))>=0)
	{
		if ((err=luaL_loadstring(m_L, fc.c_str()))==0)
		{
			zLuaObj fu(m_L,-1);
			lua_pop(m_L,1);
			zString em;
			if ((err=fu.call_va_err(&em,zlua_traceback,"V>A",pin,&ov))>=0)
			{
				return err;
			}
			lua_pushstring(m_L,em.c_str());
		}
		report(err);
	}
	return err;
}
zLuaObj ZLuaEnv::loadfile(const char* filename,EPATH p)
{
	zLuaObj thr(m_L);
	int err;
	zString fc;
	if ((err=zLoadFile(m_delegate?m_delegate->GetPath(p):"",filename,fc))>=0)
	{
		if ((err=luaL_loadstring(m_L, fc.c_str()))==0)
		{
			thr.linkTo(m_L,-1);
			lua_pop(m_L,1);
			return thr;
		}
		report(err);
	}
	return thr;
}
int ZLuaEnv::dofile(const char* filename,EPATH p)
{
	int err;
	zString fc;
	if ((err=zLoadFile(m_delegate?m_delegate->GetPath(p):"",filename,fc))>=0)
	{
		if ((err=luaL_loadstring(m_L, fc.c_str()))==0)
		{
			if ((err=zlua_docall(m_L, 0, 0))==0)
			{
				return 0;
			}
		}
		report(err);
	}
	return err;
}
int ZLuaEnv::dostring(const char* script)
{
	int err;
	zString fc;
	if (script)
	{
		if ((err=luaL_loadstring(m_L, script))==0)
		{
			if ((err=zlua_docall(m_L, 0, 0))==0)
			{
				return 0;
			}
		}
		report(err);
	}
	return err;
}
int ZLuaEnv::dolibrary(const char* name)
{
	lua_getglobal(m_L, "require");
	lua_pushstring(m_L, name);
	return report(zlua_docall(m_L, 1, 0));
}

void ZLuaEnv::setGlobalNumber(const char * name, double val)
{
	lua_pushnumber(m_L, val);
	lua_setglobal(m_L,name);
}
void ZLuaEnv::setGlobalInteger(const char * name, int val)
{
	lua_pushinteger(m_L, val);
	lua_setglobal(m_L,name);
}
void ZLuaEnv::setGlobalString(const char * name, const char * val)
{
	lua_pushstring(m_L, val);
	lua_setglobal(m_L,name);
}

const zString& ZLuaEnvDelegate::GetPath(ZLuaEnv::EPATH p)
{
	static zString s_path="./";
	return s_path;
}
int ZLuaEnvDelegate::OnCreateLuaEnv(ZLuaEnv& le)
{
	zlua_regzluaenvapi(le);
	return 0;
};

void zlua_createmeta(struct lua_State* L, const char* name, const struct luaL_Reg* apis)
{
	luaL_newmetatable(L, name);  /* create metatable for file handles */
	lua_pushvalue(L, -1);  /* push metatable */
	lua_setfield(L, -2, "__index");  /* metatable.__index = metatable */
	luaL_register(L, NULL, apis);  /* file methods */
}
void* zlua_asserttype(struct lua_State* L,int idx, const char* name)
{
	void** f = (void**)luaL_checkudata(L, idx, name);
	if (*f == NULL)
		luaL_error(L, "attempt to use a nil %s",name);
	return *f;
}
void zlua_newtype(lua_State * L, const char * name, int flags, void * data, bool own)
{
	if (data==NULL)
	{
		lua_pushnil(L);
		return;
	}
	zlua_UDProxy * u = (zlua_UDProxy *)lua_newuserdata(L, sizeof(zlua_UDProxy));

	u->data = data;
	u->flags = flags;
	u->own = own;

	luaL_newmetatable(L, name);
	lua_setmetatable(L, -2);
}
void* zlua_asserttype_data(struct lua_State* L, int idx, const char* name,int flag)
{
	zlua_UDProxy* f = (zlua_UDProxy*)luaL_checkudata(L,idx, name);
	if (f == NULL)
		luaL_error(L, "attempt to use a nil %s",name);
	if (f->flags!=flag)
		luaL_error(L, "attempt to use a bad flag ud %s(%d:%d)",name,flag,f->flags);
	return f->data;
}
void* zlua_get_type_data(struct lua_State* L, int idx, const char* name,int flag)
{
	if (lua_isnil(L,idx)) return NULL;
	zlua_UDProxy* f = (zlua_UDProxy*)luaL_checkudata(L,idx, name);
	if (f == NULL)
		luaL_error(L, "attempt to use a nil %s",name);
	if (f->flags!=flag)
		luaL_error(L, "attempt to use a bad flag ud %s(%d:%d)",name,flag,f->flags);
	return f->data;
}
void* zlua_clear_type_data(struct lua_State* L, int idx, const char* name,int flag)
{
	if (lua_isnil(L,idx)) return NULL;
	zlua_UDProxy* f = (zlua_UDProxy*)luaL_checkudata(L,idx, name);
	if (f == NULL)
		luaL_error(L, "attempt to use a nil %s",name);
	if (f->flags!=flag)
		luaL_error(L, "attempt to use a bad flag ud %s(%d:%d)",name,flag,f->flags);
	void* d=f->data;
	f->data=NULL;
	f->flags=0;
	return d;
}
int zlua_traceback(lua_State *L) 
{
	if (!lua_isstring(L, 1))  /* 'message' not a string? */
		return 1;  /* keep it intact */
	lua_getfield(L, LUA_GLOBALSINDEX, "debug");
	if (!lua_istable(L, -1)) {
		lua_pop(L, 1);
		return 1;
	}
	lua_getfield(L, -1, "traceback");
	if (!lua_isfunction(L, -1)) {
		lua_pop(L, 2);
		return 1;
	}
	lua_pushvalue(L, 1);  /* pass error message */
	lua_pushinteger(L, 2);  /* skip this function and traceback */
	lua_call(L, 2, 1);  /* call debug.traceback */
	return 1;
}

int zlua_docall (lua_State *L, int narg, int mret)
{
	int status;
	int base = lua_gettop(L) - narg;  /* function index */
	lua_pushcfunction(L, zlua_traceback);  /* push traceback function */
	lua_insert(L, base);  /* put it under chunk and args */
	status = lua_pcall(L, narg, mret, base);
	lua_remove(L, base);  /* remove traceback function */
	/* force a complete garbage collection in case of errors */
	if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
	return status;
}
#if 0
#define CO_RUN	0	/* running */
#define CO_SUS	1	/* suspended */
#define CO_NOR	2	/* 'normal' (it resumed another coroutine) */
#define CO_DEAD	3

static int costatus (lua_State *L, lua_State *co) {
	if (L == co) return CO_RUN;
	switch (lua_status(co)) {
		case LUA_YIELD:
			return CO_SUS;
		case 0: {
			lua_Debug ar;
			if (lua_getstack(co, 0, &ar) > 0)  /* does it have frames? */
				return CO_NOR;  /* it is running */
			else if (lua_gettop(co) == 0)
				return CO_DEAD;
			else
				return CO_SUS;  /* initial state */
						}
		default:  /* some error occured */
			return CO_DEAD;
	}
}
static const char *const statnames[] =
{"running", "suspended", "normal", "dead"};

int zlua_resume(struct lua_State *L,lua_State *co, int narg)
{
	int status = costatus(L, co);
	if (!lua_checkstack(co, narg))
		luaL_error(L, "too many arguments to resume");
	if (status != CO_SUS) {
		lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);
		return -1;  /* error flag */
	}
	lua_xmove(L, co, narg);
//	lua_setlevel(L, co);
	status = lua_resume(co, narg);
	if (status == 0 || status == LUA_YIELD) {
		int nres = lua_gettop(co);
		if (!lua_checkstack(L, nres + 1))
			luaL_error(L, "too many results to resume");
		lua_xmove(co, L, nres);  /* move yielded values */
		return nres;
	}
	else {
		lua_xmove(co, L, 1);  /* move error message */
		return -1;  /* error flag */
	}
}
#endif
ZLuaThrRun::ZLuaThrRun(ZLuaEnv& le)
:m_le(le)
{
	m_le.dolibrary("zutil.thrrun");
	zLuaObj zutil(le.m_L,"zutil");
	zLuaObj thrrun(zutil,"thrrun");
	m_firetick.linkTo(thrrun,"FireTick");
	m_fireevent.linkTo(thrrun,"FireEvent");
	m_newthread.linkTo(thrrun,"NewThread");
}
int ZLuaThrRun::fireTick()
{
	int r,re;
	re=m_firetick.call_va_err(NULL,zlua_traceback,">b",&r);
	return re>=0?r:re;
}
int ZLuaThrRun::fireEvent(const char* name,const char* arg)
{
	int r,re;
	re=m_fireevent.call_va_err(NULL,zlua_traceback,"ss>b",name,arg,&r);
	return re>=0?r:re;
}
int ZLuaThrRun::newThreadFile(const char* name,const char* filename)
{	
	zLuaObj thr=m_le.loadfile(filename);
	return m_newthread.call_va_err(NULL,zlua_traceback,"sO",name,&thr);
}
//////////////////////////////////

static int lua_zenv_getpath(lua_State* L)
{
	ZLuaEnv *f = *(ZLuaEnv **)lua_touserdata(L, lua_upvalueindex(1));
	if (!f)
		return luaL_error(L,"internal error");
	int pt=ZLuaEnv::ZPATH_DEFAULT;
	int type=lua_type(L,1);
	if (type==LUA_TNONE || type == LUA_TNIL)
	{
	}
	else if (type==LUA_TNUMBER)
	{
		pt=luaL_checkinteger(L,1);
	}
	else if (type==LUA_TSTRING)
	{		
		pt=zlua_Name2EPATH(lua_tostring(L,1));
	}
	else
	{
		return luaL_typerror(L,1,"nil|number|string");
	}
	const zString& pp=f->GetPath((ZLuaEnv::EPATH)pt);
	lua_pushlstring(L,pp,pp.length());
	return 1;
}

void zlua_regzluaenvapi(ZLuaEnv& le)
{
	static const luaL_reg scriptLib[] = 
	{
		{"getpath",lua_zenv_getpath},
		{NULL, NULL}
	};
	ZLuaEnv** p=(ZLuaEnv**)lua_newuserdata(le,sizeof(ZLuaEnv*));
	*p=&le;
	luaI_openlib(le, "zutil",scriptLib,1);
	lua_pop(le,1);
}
